package cn.edu.xmu.datamining.tangzk.mralgos.parallelbfs;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.hadoop.io.Writable;

import cn.edu.xmu.datamining.tangzk.util.Joiner;

public class GraphNodeWritable implements Writable {

	// Node type: 0 indicates Node structure, 1 is shortest distance
	private int type;
	private int distance;
	private List<Integer> adjNodes;

	public GraphNodeWritable() {
		this.type = 0;
		this.distance = 0;
		this.adjNodes = null;
	}

	public GraphNodeWritable(GraphNodeWritable graphNode) {
		this.type = graphNode.type;
		this.distance = graphNode.distance;
		if (graphNode.adjNodes != null) {
			this.adjNodes = new ArrayList<Integer>(graphNode.adjNodes.size());
			for (int i : graphNode.adjNodes) {
				this.adjNodes.add(i);
			}
		}
	}

	public GraphNodeWritable(String graphStruct) {
		// format: distance: id1, id2
		String[] flds = graphStruct.split(":");
		this.type = 0;
		if ("MAX".equals(flds[0].trim().toUpperCase())) {
			this.distance = Integer.MAX_VALUE;
		} else {
			this.distance = Integer.valueOf(flds[0]);
		}
		String[] ids = flds[1].split(",");
		if (ids.length > 0) {
			adjNodes = new ArrayList<Integer>();
			for (int i = 0; i < ids.length; i++) {
				adjNodes.add(Integer.valueOf(ids[i]));
			}
		}
	}

	public GraphNodeWritable(int distance) {
		this.type = 1;
		this.distance = distance;
	}

	public boolean isNode() {
		return this.type == 0;
	}

	@Override
	public void readFields(DataInput in) throws IOException {
		this.type = in.readInt();
		this.distance = in.readInt();
		if (this.type == 0) {
			// read graph structure
			int l = in.readInt();
			if (l > 0) {
				this.adjNodes = new ArrayList<Integer>();
				for (int i = 0; i < l; i++) {
					this.adjNodes.add(in.readInt());
				}
			}
		}
	}

	@Override
	public void write(DataOutput out) throws IOException {
		out.writeInt(this.type);
		out.writeInt(this.distance);
		if (this.type == 0) {
			// output graph structure
			out.writeInt(this.adjNodes.size());
			for (int id : this.adjNodes) {
				out.writeInt(id);
			}
		}
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("Dist:").append(this.distance);
		if (this.isNode()) {
			sb.append(" Node:").append(Joiner.on(adjNodes.iterator(), ","));
		}
		return sb.toString();
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof GraphNodeWritable)) {
			return false;
		}
		GraphNodeWritable node1 = (GraphNodeWritable) obj;
		return (this.type == node1.type) && (this.distance == node1.distance);
	}

	@Override
	public int hashCode() {
		// TODO
		return this.type << 24 + this.distance;
	}

	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}

	public int getDistance() {
		return distance;
	}

	public void setDistance(int distance) {
		this.distance = distance;
	}

	public List<Integer> getAdjNodes() {
		return adjNodes;
	}

	public void setAdjNodes(List<Integer> adjNodes) {
		this.adjNodes = adjNodes;
	}

}
